package com.example.javaproject.thread.juc.blockingqueue;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Description: 有界队列的实现
 * <br/>Author:  tianjinyong
 * <br/>Date:    2020/7/10 5:15 PM
 */
public class ImpArrayBlockingQueue {

    /**
     * 模拟ArrayBlockingQueue的实现 (FIFO 先进先出)
     */
    static class MArrayBlockingQueue<E> {

        /**
         * 存储数据的数组
         */
        private Object[] items;

        /**
         * 容量
         */
        private int capacity;

        /**
         * 并发控制的锁和条件
         */
        private ReentrantLock lock;
        private Condition notFull;
        private Condition notEmpty;

        /**
         * 当前队列中元素个数
         */
        private int count;

        /**
         * 存数据的下标
         */
        private int putIndex;

        /**
         * 取数据的下标
         */
        private int takeIndex;

        public MArrayBlockingQueue(int capacity) {
            this.capacity = capacity;
            lock = new ReentrantLock();
            notEmpty = lock.newCondition();
            notFull = lock.newCondition();
            items = new Object[this.capacity];
        }

        /**
         * 向队列中添加数据,如果数据已满不允许插入数据
         *
         * @param e
         */
        public void put(E e) {
            ReentrantLock putLock = lock;
            putLock.lock();

            try {
                // 如果队列数据满了，调用put的线程在这里进行wait
                while (count >= capacity)
                    notFull.await();

                // 添加元素
                items[putIndex] = e;
                count++;
                putIndex++;

                // 如果队列满了，那么putIndex值需要值为0，因为取出数据过后还能继续添加数据
                if (putIndex == capacity)
                    putIndex = 0;

                //添加元素完成队列就不为空了
                notEmpty.signalAll();
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                putLock.unlock();
            }
        }

        /**
         * 取出数据
         */
        public E take() {
            ReentrantLock takeLock = this.lock;
            takeLock.lock();
            try {
                while (count == 0)
                    notEmpty.await();
                // 开始取数据
                E e = (E) items[takeIndex];
                items[takeIndex] = null;
                takeIndex++;
                count--;

                if (takeIndex == capacity)
                    takeIndex = 0;
                notFull.signalAll();
                return e;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                takeLock.unlock();
            }

            return null;
        }
    }

    public static void main(String[] args) {
        MArrayBlockingQueue<Integer> queue = new MArrayBlockingQueue<>(5);

        for (int i = 0; i < 1; i++) {
            final int f = i;
            new Thread(() -> {
                for (int j = f; j < f + 5; j++) {
                    queue.put(j);
                    System.out.println(Thread.currentThread().getName() + "存数据，值为 " + j);
                }
            }).start();
        }

        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (; ; ) {
                        System.out.println(Thread.currentThread().getName() + "取数据，值为 " + queue.take());
                    }
                }
            }).start();
        }

    }
}
